Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement parsing for records #40467

Merged
merged 7 commits into from
Jan 5, 2020
Merged

Conversation

agocke
Copy link
Member

@agocke agocke commented Dec 18, 2019

I recommend reviewing the first commit separately. All it does is split DeclarationModifiers into its own field in our flags structs since we ran out of space, and splitting the modifiers out was simpler and clearer. The second commit actually implements the parsing.

As agreed in LDM, the specification requires that a data contextual
keyword be present in front of the class and a parameter list to be
present after the identifier, for the type to be a record.

The absence of either of these two elements will be a semantic error,
not a parsing error.

Relates to #40726 (test plan for records)

@agocke agocke force-pushed the record-syntax branch 2 times, most recently from 282e10a to 6d069ea Compare December 18, 2019 23:12
@agocke agocke changed the title Record syntax Implement parsing for records Dec 18, 2019
@agocke agocke force-pushed the record-syntax branch 2 times, most recently from 73ed2cc to 336907b Compare December 25, 2019 05:05
@agocke agocke force-pushed the record-syntax branch 5 times, most recently from 83ff132 to 5b6c6dd Compare December 26, 2019 21:30
As agreed in LDM, the specification requires that a data contextual
keyword be present in front of the class and a parameter list to be
present after the identifier, for the type to be a record.

The absence of either of these two elements will be a semantic error,
not a parsing error.
@agocke agocke marked this pull request as ready for review December 27, 2019 21:43
@agocke agocke requested review from a team as code owners December 27, 2019 21:43
//
// | |d|yy|xxxxxxxxxxxxxxxxxxxxxxx|wwwwww|
// | |vvv|zzzz|f|d|yy||wwwwww|
Copy link
Member

@333fred 333fred Dec 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

| [](start = 48, length = 1)

Extra vertical bar #Closed

@@ -1485,6 +1527,8 @@ private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxLis
semicolon);

case SyntaxKind.InterfaceKeyword:
RoslynDebug.Assert(openBrace != null);
Copy link
Member

@gafter gafter Dec 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RoslynDebug [](start = 24, length = 11)

I don't see how this assertion is correct. It appears that the above code would accept interface A; I think we either need to make the curly braces optional or make missing tokens for them with an error. #Closed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly this code appears to be dropping the parameter list on the floor. I think it needs to be either added to the syntax, not permitted by the parser, or you need to build a class declaration when that happens.


In reply to: 361761780 [](ancestors = 361761780)

parameterList,
baseList,
constraintClauses,
openBraceToken: default,
Copy link
Member

@gafter gafter Dec 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default [](start = 32, length = 7)

I suspect you intended this to be the creation of a fresh semicolon token if there were no members, or fresh brace tokens if there were. #Closed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, since you don't know the language version at this point, perhaps this should create brace tokens always.


In reply to: 361762456 [](ancestors = 361762456)

default,
members,
default,
default);
Copy link
Member

@gafter gafter Dec 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default [](start = 16, length = 7)

I suspect you intended this to be the creation of a fresh semicolon token if there were no members, or fresh brace tokens if there were.

@@ -156,7 +156,7 @@ public void DeeplyNestedGeneric()
(ExecutionArchitecture.x86, ExecutionConfiguration.Debug) => 270,
(ExecutionArchitecture.x86, ExecutionConfiguration.Release) => 1290,
(ExecutionArchitecture.x64, ExecutionConfiguration.Debug) => 170,
(ExecutionArchitecture.x64, ExecutionConfiguration.Release) => 730,
(ExecutionArchitecture.x64, ExecutionConfiguration.Release) => 700, // PROTOTYPE(angocke): Improve stack usage
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// PROTOTYPE(angocke): Improve stack usage [](start = 84, length = 42)

I wouldn't bother.

public class RecordTests : CompilingTestBase
{
[Fact]
public void RecordLanguageVersion()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RecordLanguageVersion [](start = 20, length = 21)

Also try class Point;. That should not be permitted in older language versions either.

@gafter
Copy link
Member

gafter commented Dec 27, 2019

Do you have a document that describes the feature this is intended to implement? Neither https://github.com/dotnet/csharplang/blob/master/proposals/records.md nor https://github.com/dotnet/csharplang/blob/master/proposals/recordsv2.md seems to be right.

@gafter
Copy link
Member

gafter commented Dec 27, 2019

One of the things I'd be looking for in the specification is how you will modify the syntax and parser to handle constructor parameters of the base type. Without knowing how that's going to be handled, I cannot know if these parser changes implement the intended design.


In reply to: 569365350 [](ancestors = 569365350)

@gafter
Copy link
Member

gafter commented Dec 27, 2019

Finished review pass (Iteration 2).

@@ -17,7 +17,7 @@
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<ToolsetPackagesDir>$(RepoRoot)build\ToolsetPackages\</ToolsetPackagesDir>

<RoslynPortableTargetFrameworks>net472;netcoreapp2.1</RoslynPortableTargetFrameworks>
<RoslynPortableTargetFrameworks>netcoreapp2.1;net472</RoslynPortableTargetFrameworks>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change. Quoting from @jaredpar a few months ago:

We need to keep net472 as the first TF listed because that controls items like which tests run by default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's the point. We should swap the default. I can't test on Mac using VS Code unless it's first.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should swap the default. I can't test on Mac using VS Code unless it's first.

Definitely don't want us to do this. As @333fred noted, the order here is deliberate. Changing it will not fix anything, instead it will just fix one scenario and break another. In this case it would be fixing Mac, the minority scenario, and breaking Windows, the dominant scenario.

Where is the bug on VS Code that prevents Mac testing here? Have you filed a bug?

Copy link
Member Author

@agocke agocke Jan 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it break Windows? The test explorer seems to work fine. Doesn't look like there's a bug here for VS Code. They don't support debugging on anything but .NET Core, and switching between target frameworks isn't a bug, it's a feature they don't have.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it break Windows?

The set of tests run for desktop and core are significantly different. Moving to core by default will break the expectations of developers that expect to get the desktop tests.

If you think the trade off is a good one here then the correct course of action is to discuss the policy with the team. Not change it subtly in a PR. Particularly since this has come up several times before and we've kept the current order for these reasons.

Doesn't look like there's a bug here for VS Code.

VS Code cannot test code that every other tool in our toolbox can test. Is the explicit design of VS Code that they can't support multi-targeted projects? If so that seems really unfortunate.

it's a feature they don't have.

Where is the issue tracking the feature request?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think the trade off is a good one here then the correct course of action is to discuss the policy with the team. Not change it subtly in a PR. Particularly since this has come up several times before and we've kept the current order for these reasons.

Yup, this is just going into a feature branch. I had planned to send an email out before changing it in master. Can I leave a PROTOTYPE comment above and we can do that when I'm back in Seattle?

Where is the issue tracking the feature request?

One thing we have to change for that is running our desktop tests as 64-bit by default. For the life of me I cannot figure out how to change that from 32-bit. I gave up after a couple hours.

dotnet/vscode-csharp#1716

You aren't doing anything wrong. x86 debugging is not supported by the C# extension. You will need full Visual Studio for that.

Beyond that, I'm not sure what feature we would be asking for. Some way to change the framework the tests are run under? VS only got that a couple months ago, so it's not exactly something I'd put at the top of the VS Code priority list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I leave a PROTOTYPE comment above and we can do that when I'm back in Seattle?

That's fine by me.

Beyond that, I'm not sure what feature we would be asking for. Some way to change the framework the tests are run under?

I think the ask is pretty straight forward: we have a project which is multi-targeted to desktop and .NET Core. When that happens don't fail, instead prefer the .NET Core target framework. Basically do the only action which is capable of succeeding in this environment.

@333fred
Copy link
Member

333fred commented Dec 31, 2019

Done review pass (commit 2)

SyntaxToken semicolon;
SyntaxToken? openBrace;
SyntaxToken? closeBrace;
if (isInterface || CurrentToken.Kind != SyntaxKind.SemicolonToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CurrentToken.Kind != SyntaxKind.SemicolonToken [](start = 35, length = 46)

I feel like this isn't a great solution. I know this change was prompted by Neal, but I feel like a better solution would be to parse the interface as a record declaration, and then issue a semantic error for "Interfaces cannot be records."

comp.VerifyDiagnostics();
comp = CreateCompilation(src2, parseOptions: TestOptions.RegularPreview);
comp.VerifyDiagnostics();
comp = CreateCompilation(src3, parseOptions: TestOptions.RegularPreview);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a VerifyDiagnostics

@333fred
Copy link
Member

333fred commented Jan 2, 2020

Done review pass (commit 5)

@gafter
Copy link
Member

gafter commented Jan 2, 2020

Similarly, I would expect to see a syntax tree and parsing for the "constructor body" from https://github.com/dotnet/csharplang/blob/master/proposals/records.md#primary-constructor-body (which incidentally was copied verbatim into Java's record feature).


In reply to: 569365766 [](ancestors = 569365766,569365350)

Copy link
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good as a partial implementation of parsing for what I expect a record specification to look like based on recent discussions. If my expectations are wrong then I'd want to review this code again with respect to whatever the specification actually is. To save us all from having to re-review code going forward, please provide a specification of this feature.

@agocke
Copy link
Member Author

agocke commented Jan 3, 2020

@gafter Good point, I've added https://github.com/dotnet/csharplang/blob/master/proposals/records-wip.md

Basically, I intend to support the syntax that you linked, with the added data modifier, but I'm trying to carve out a few minimal work items to avoid making giant PRs.

@jcouv jcouv added this to the Compiler.Net5 milestone Jan 3, 2020
@agocke agocke merged commit 8769e0b into dotnet:features/records Jan 5, 2020
@agocke agocke deleted the record-syntax branch January 5, 2020 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants